package com.aliyun.messagemgt.application.service.media;

import com.aliyun.messagemgt.util.redis.Redis;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

import java.util.List;

/**
 * Created with IDEA
 *
 * @author:zhangxingxing 0027012120
 * @date:2019/1/31
 * @time:11:38
 */
@Component
public class BloomFilterImpl implements BloomFilter {

    private static final String KEY = "URL_BLOOMFILTER";
    /**
     * 用于生成信息指纹的8个随机数，最好选取质数
     */
    private static final int[] SEEDS = new int[]{3, 5, 7, 11, 13, 31, 37, 61};
    /**
     * 用于存储8个随机哈希值对象
     */
    private Hash[] func = new Hash[SEEDS.length];
    /**
     * 二进制向量的位数，相当于能存储1000万条url左右，误报率为千万分之一 （20倍左右）
     */
    private static final int BIT_SIZE = 2 << 28;

    /**
     * 构造器中初始化 hash函数
     */
    public BloomFilterImpl() {
        for (int i = 0; i < SEEDS.length; i++) {
            func[i] = new Hash(BIT_SIZE, SEEDS[i]);
        }
    }

    @Override
    public void addValue(String value) {
        //将字符串value哈希为8个或多个整数，然后在这些整数的bit上变为1
        if (value != null) {
            Jedis jedis = Redis.getJedis();
            Pipeline pipeline = jedis.pipelined();
            for (Hash f : func) {
                pipeline.setbit(KEY, f.hash(value), true);
            }
            pipeline.sync();
            jedis.close();
        }
    }

    @Override
    public boolean contains(String value) {
        if (value == null) {
            return false;
        }
        Jedis jedis = Redis.getJedis();
        Pipeline pipeline = jedis.pipelined();
        boolean result = false;
        //将要比较的字符串重新以上述方法计算hash值，再与布隆过滤器比对
        for (Hash f : func) {
            pipeline.getbit(KEY, f.hash(value));
        }
        List<Object> objectList = pipeline.syncAndReturnAll();
        for (Object ob: objectList) {
            if(!ob.equals(false)){
               result = true;
            }else{
                result =false;
                break;
            }
        }
        jedis.close();
        return result;
    }

    @Override
    public boolean containAndAdd(String value) {
        boolean res = contains(value);
        if (!res) {
            addValue(value);
        }
        return res;
    }

    /**
     * 随机哈希值对象
     */
    public static class Hash {
        /**
         * 二进制向量数组大小
         */
        private int size;
        /**
         * 随机数种子
         */
        private int seed;

        public Hash(int cap, int seed) {
            this.size = cap;
            this.seed = seed;
        }

        /**
         * 计算哈希值(也可以选用别的恰当的哈希函数)
         */
        public int hash(String value) {
            int result = 0;
            int len = value.length();
            for (int i = 0; i < len; i++) {
                result = seed * result + value.charAt(i);
            }
            return (size - 1) & result;
        }
    }

}
